package com.example.netcamerastreamdemo;

import android.content.Intent;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.google.gson.Gson;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

@RequiresApi(api = Build.VERSION_CODES.O)
public class WebSocketThreadActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    private static final String TAG = "WebSocketThreadActivity";

    WebSocket webSocket;
    Gson mGson = new Gson();
    static String dev_guid = "63-00F628C3B402-9A2A";
    static int video_chnn = 9;
    static int sub_chnn = 2;
    static String rec_path = "63-00F628C3B402-9A2A\\\\1623202389634\\\\63-00F628C3B402-9A2A_9_1623202389634";

    FramData mFramData;
    int mIndex = -1;
    SurfaceView surfaceView;
    boolean isFinished = false;
    File file;
    FileOutputStream fileOutputStream;
    private MediaCodec.BufferInfo mBufferInfo;
    private MediaCodec decoder;
    int mWidth = 704;
    int mHeight = 576;
    //        int mWidth = 640;
//        int mHeight = 480;
    int TIMEOUT_US = 0 ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        surfaceView = findViewById(R.id.surface_view);
        surfaceView.getHolder().addCallback(this);
//        requestPermissions(new String[]{
//                Manifest.permission.READ_EXTERNAL_STORAGE,
//                Manifest.permission.WRITE_EXTERNAL_STORAGE
//        }, 0);
//        file = new File(getExternalFilesDir(null) + "/" + System.currentTimeMillis() + ".h264");
//        try{
//            fileOutputStream = new FileOutputStream(file);
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
        testByte();
    }

    private void testByte(){
        ByteBuffer buffer = ByteBuffer.allocate(102400);
        Log.i(TAG, "---buffer=1==" + buffer.toString());
        byte[] b1 = new byte[32];
        buffer.put(b1);
        Log.i(TAG, "---buffer=2==" + buffer.toString());
        byte[] b2 = new byte[63];
        buffer.put(b2);
        Log.i(TAG, "---buffer=3==" + buffer.toString());

        buffer = ByteBuffer.wrap(b2, 10, 10);
        Log.i(TAG, "---buffer=4==" + buffer.toString());

    }

    public void initWebSocket(View view){
        OkHttpClient mClient = new OkHttpClient.Builder()
                .readTimeout(3, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(3, TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(3, TimeUnit.SECONDS)//设置连接超时时间
                .build();

        String url = "wss://shike-qa.video110.cn/nsdk";
        Request request = new Request.Builder().get().url(url).build();
        webSocket = mClient.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                super.onOpen(webSocket, response);
                Log.i(TAG, "---onOpen===");
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                super.onMessage(webSocket, text);
                Log.i(TAG, "---onMessage===" + text);
            }

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                super.onMessage(webSocket, bytes);
                mIndex++;
                Log.i(TAG, "---onMessage===" + mIndex);
                mFramData = new FramData(bytes.substring(44).toByteArray(), mIndex);
//                try {
//                    fileOutputStream.write(videoBytes);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
                if (mIndex == 0){
                    prepare(mFramData);
                }
                decode(mFramData);
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                super.onClosing(webSocket, code, reason);
                Log.i(TAG, "---onClosing===" + reason);
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                super.onClosed(webSocket, code, reason);
                Log.i(TAG, "---onClosed===" + reason);
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                super.onFailure(webSocket, t, response);
                Log.i(TAG, "---onFailure===" + t.toString());
            }
        });
    }

    void videoIsPlaying(){

    }

    public void test1(View view){
    }

    public void stopWebsocket(View view){
        if (webSocket == null){
            return;
        }
        webSocket.cancel();
    }

    public void sendStartVideoMsg(View view){
        MsgData startVideoMsg = new MsgData(
                "VSDK_CMD_TX_RT_VIDEO_START",
                dev_guid,
                video_chnn,
                sub_chnn,
                rec_path);
        webSocket.send(mGson.toJson(startVideoMsg));
        videoIsPlaying();
    }

    public void sendStopVideoMsg(View view){
        MsgData stopVideoMsg = new MsgData(
                "VSDK_CMD_TX_RT_VIDEO_STOP",
                dev_guid,
                video_chnn,
                sub_chnn,
                rec_path);
        webSocket.send(mGson.toJson(stopVideoMsg));
        isFinished = true;
//        firstBytes = null;
//        try {
//            fileOutputStream.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
    }

    private void prepare(FramData framData){
        Log.i(TAG, "---prepare===" + framData.index);
        int pos=0;
        byte[] spspps = framData.data;

        if(!((pos+3<spspps.length)&&(spspps[pos]==0&&spspps[pos+1]==0&&spspps[pos+2]==0&&spspps[pos+3]==1))){
//                return false;
        }else {
            //00 00 00 01开始标志后的一位
            pos=4;
        }
        while((pos+3<spspps.length)&&!(spspps[pos]==0&&spspps[pos+1]==0&&spspps[pos+2]==0&&spspps[pos+3]==1)){
            pos++;
        }
        byte[] mSps = Arrays.copyOfRange(spspps, 0, pos);
        byte[] mPps = Arrays.copyOfRange(spspps, pos, spspps.length);
        mBufferInfo = new MediaCodec.BufferInfo();
        MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
//            format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 9999999);
//            format.setInteger(MediaFormat.KEY_MAX_HEIGHT, mHeight);
//            format.setInteger(MediaFormat.KEY_MAX_WIDTH, mWidth);
//            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, -1);
//            format.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
//            format.setInteger(MediaFormat.KEY_BIT_RATE, mWidth * mHeight * 15);
//        format.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);

        format.setByteBuffer("csd-0", ByteBuffer.wrap(mSps));
        format.setByteBuffer("csd-1", ByteBuffer.wrap(mPps));
//            Log.i(TAG, "---mSps===" + mSps);
//            Log.i(TAG, "---mSps===" + ByteBuffer.wrap(mSps));
//            Log.i(TAG, "---mPps===" + mPps);
//            Log.i(TAG, "---mPps===" + ByteBuffer.wrap(mPps));

        try {
            decoder = MediaCodec.createDecoderByType("video/avc");
        } catch (IOException e) {
            e.printStackTrace();
        }
        decoder.configure(format, surfaceView.getHolder().getSurface(), null, 0);
        decoder.start();
    }

    private void decode(FramData framData){
        if (isFinished){
            return;
        }
        //            Log.i(TAG, "---decode===");
        boolean isEnd = false;
        int count = 0;
        while (!isEnd){
            int inIndex = decoder.dequeueInputBuffer(TIMEOUT_US);
            Log.i(TAG, "---inIndex===" + inIndex);
            if (inIndex >= 0){
                ByteBuffer buffer = decoder.getInputBuffer(inIndex);
                if (buffer == null)return;
                buffer.clear();
                byte[] b = framData.data.clone();

                if (b == null) {
                    Log.i(TAG, "---mFramData.data == null===");
                    decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                }else {
                    Log.i(TAG, "---put.data===" + framData.index);
                    buffer.put(b, 0, b.length);
                    decoder.queueInputBuffer(inIndex, 0, b.length, 0, MediaCodec.BUFFER_FLAG_KEY_FRAME);
                }
            }else {
                count++;
                if (count > 3) {
                    count = 0;
                    isEnd = true;
                }
            }
            int outIndex = decoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
            while (outIndex >= 0){
                decoder.releaseOutputBuffer(outIndex, true);
                outIndex = decoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
            }
        }
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
        Log.i(TAG, "---surfaceCreated===");
//        workThread.start();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        Log.i(TAG, "---surfaceChanged===");

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
        Log.i(TAG, "---surfaceDestroyed===");
    }

    public static class FramData{
        byte[] data;
        int index;
        long time;

        public FramData(byte[] data, int index) {
            this.data = data;
            this.index = index;
            this.time = System.nanoTime();
        }
    }

    public static class MsgData{
        public MsgData(String cmd, String dev_guid, int video_chnn, int sub_chnn, String rec_path) {
            this.cmd = cmd;
            this.dev_guid = dev_guid;
            this.video_chnn = video_chnn;
            this.sub_chnn = sub_chnn;
            this.rec_path = rec_path;
        }

        String cmd;
        String dev_guid;
        int video_chnn;
        int sub_chnn;
        String rec_path;
    }
}